//////////////////////////////////////////////////
// EASIM project
// easim.omar.TimeStats
// 6/21/2007 9:44 PM
// Cong Liu
// gzcong@gmail.com

// Copyright (C) 2007
// All rights reserved.
// Refer to LICENSE of JIST (Java In Simulation Time) for terms and conditions of use.

package easim.omar;

import easim.net.Clock;
import easim.net.Timer;

import jist.swans.Constants;
import jist.runtime.JistAPI.DoNotRewrite;

public class TimeStats implements Timer, DoNotRewrite
{

  public static final long TIME_SLOT = 3 * Constants.SECOND; 
  public static final int TOTAL_SLOTS = 10; 

  protected static class Traffic implements DoNotRewrite
  {
    public double dtdvMsgSize = 0;
    public double dtdvSendDataMsgSize = 0;
    public double dtdvRecvDataMsgSize = 0;
    public double dtdvExpSendDataMsgSize = 0;
    public double swSendDataMsgSize = 0;
    public double swRecvDataMsgSize = 0;
    public double swExpSendDataMsgSize = 0;
    public int dtdvMaxHop = 0;

    public void increase(Traffic t) {
      dtdvMsgSize += t.dtdvMsgSize;
      dtdvSendDataMsgSize += t.dtdvSendDataMsgSize;
      dtdvRecvDataMsgSize += t.dtdvRecvDataMsgSize;
      dtdvExpSendDataMsgSize += t.dtdvExpSendDataMsgSize;
      swSendDataMsgSize += t.swSendDataMsgSize;
      swRecvDataMsgSize += t.swRecvDataMsgSize;
      swExpSendDataMsgSize += t.swExpSendDataMsgSize;
    }

    public void decrease(Traffic t) {
       dtdvMsgSize -= t.dtdvMsgSize;
      dtdvSendDataMsgSize -= t.dtdvSendDataMsgSize;
      dtdvRecvDataMsgSize -= t.dtdvRecvDataMsgSize;
      dtdvExpSendDataMsgSize -= t.dtdvExpSendDataMsgSize;
      swSendDataMsgSize -= t.swSendDataMsgSize;
      swRecvDataMsgSize -= t.swRecvDataMsgSize;
      swExpSendDataMsgSize -= t.swExpSendDataMsgSize;
   }

    public void incDtdvMsg(int size) {
      dtdvMsgSize += size;
    }

    public void incDtdvSendDataMsg(int size) {
      dtdvSendDataMsgSize += size;
    }

    public void incSwSendDataMsg(int size) {
      swSendDataMsgSize += size;
    }

    public void incRecvDataMsg(int size, int tickets, int hops) {
      /*
      double sw = Data.DATA_TICKETS / (double)tickets + 1;
      if (Data.DATA_TICKETS == tickets && hops > 0) {
        sw = 0;
      }
      else if (hops > 0 && sw >= 1) {
        sw -= 1;
      }
      double swp = sw / (sw + hops);
      double dtdvp = hops / (sw + hops);
      if (dtdvp > 0) {
        dtdvRecvDataMsgSize += size * dtdvp;
        dtdvExpSendDataMsgSize += size * hops;
      }
      if (sw > 0) {
        swRecvDataMsgSize += size * swp;
        swExpSendDataMsgSize += size * sw;
      }
      /**/
      if (hops > 0) {
        dtdvRecvDataMsgSize += size;
      }
      if (hops == 0 || tickets < Data.DATA_TICKETS) {
        swRecvDataMsgSize += size;
      }
      if (dtdvMaxHop < hops) {
        dtdvMaxHop = hops;
      }
      /*/ test
      if (hops > 0) {
        System.out.println("DTDV size += " + size);
      }
      if (hops == 0 || tickets < Data.DATA_TICKETS) {
        System.out.println("SW size += " + size);
      }
      if (dtdvMaxHop < hops) {
        System.out.println("DTDV hop count = " + hops);
      }
      /**/
    }

    public double dtdvUtility() {
      double R1 = dtdvRecvDataMsgSize;
      double B1 = dtdvSendDataMsgSize + dtdvMsgSize;
      double C1 = 0;
      if (B1 != 0) {
        C1 = 0.667 * R1 / Math.pow(B1, 0.667);
      }
      else {
        return 0;
      }
      double U1 = C1 * Math.pow(B1, -0.333);
      return U1;
      //double dtdvSend = dtdvSendDataMsgSize;
      /*
      if (dtdvSend < dtdvExpSendDataMsgSize) {
        dtdvSend = dtdvExpSendDataMsgSize;
      }
      /**/
      /*
      if (dtdvSend + dtdvMsgSize == 0) return 1;
      double u = dtdvRecvDataMsgSize / (dtdvSend + dtdvMsgSize);
      if (u > 1) return 1;
      return u;
      /**/
    }

    public double swUtility() {
      double R2 = swRecvDataMsgSize;
      double B2 = swSendDataMsgSize;
      double NG = Data.DATA_SIZE * Data.DATA_INTERVAL * TOTAL_SLOTS / (double)TIME_SLOT;
      double C2 = 0;
      if (R2 >=  NG) {
        //System.out.println("R2=" + R2);
        //System.out.println("NG=" + NG);
        return 0;
      }
      if (B2 != 0) {
        C2 = -NG / B2 * Math.log(1 - R2 / NG);
      }
      else {
        return 0;
      }
      double U2 = C2 * Math.exp(- C2 * B2 / NG);
      return U2;
      //double swSend = swSendDataMsgSize;
      /*
      if (swSend < swExpSendDataMsgSize) {
        swSend = swExpSendDataMsgSize;
      }
      /**/
      /*
      if (swSend == 0) return 1;
      double u = swRecvDataMsgSize / swSend;
      if (u > 1) return 1;
      return u;
      /**/
    }

    public double currentDtdvAllocationPoint() {
      /*
      double dtdvSend = dtdvSendDataMsgSize;
      if (dtdvSend < dtdvExpSendDataMsgSize) {
        dtdvSend = dtdvExpSendDataMsgSize;
      }
      double swSend = swSendDataMsgSize;
      if (swSend < swExpSendDataMsgSize) {
        swSend = swExpSendDataMsgSize;
      }
      double total = dtdvSend + dtdvMsgSize + swSend;
      if (total == 0) return 0.5;
      return (dtdvSend + dtdvMsgSize) / total;
      /**/
      double total = dtdvSendDataMsgSize + dtdvMsgSize + swSendDataMsgSize;
      if (total == 0) return 0.5;
      return (dtdvSendDataMsgSize + dtdvMsgSize) / total;
   }

    public double currentDtdvAllocationPoint2() {
      double total = dtdvSendDataMsgSize + swSendDataMsgSize;
      if (total == 0) return 0.5;
      return (dtdvSendDataMsgSize) / total;
   }

    public double newDtdvAllocationPoint() {
      /*
      double dtdvAP = currentDtdvAllocationPoint();
      double swAP = 1 - dtdvAP;
      double dtdvU = dtdvUtility();
      double swU = swUtility();
      if (dtdvU < swU) {
        double dtdvNewAP = dtdvAP / (1 + swU - dtdvU);
        return dtdvNewAP;
      }
      else {
        double swNewAP = swAP / (1 + dtdvU - swU);
        return 1 - swNewAP;
      }
      /**/
      /*
      double R1 = dtdvRecvDataMsgSize;
      double R2 = swRecvDataMsgSize;
      double B1 = dtdvSendDataMsgSize + dtdvMsgSize;
      double B2 = swSendDataMsgSize;
      double C = 0;
      if (B1 != 0) {
        C = 0.667 * R1 / Math.pow(B1, 0.667);
      }
      double U2 = 0;
      if (B2 != 0) {
        U2 = R2 / B2;
      }
      if (C == 0 || U2 == 0) {
        return 0.5;
      }
      double B1p = Math.pow(U2, -3) / C;
      if (B1p < 0.1) return 0.1;
      if (B1p > 0.9) return 0.9;
      return B1p;
      /**/
      double R1 = dtdvRecvDataMsgSize;
      double R2 = swRecvDataMsgSize;
      double B1 = dtdvSendDataMsgSize + dtdvMsgSize;
      double B2 = swSendDataMsgSize;
      double C1 = 0;
      if (B1 != 0) {
        C1 = 0.667 * R1 / Math.pow(B1, 0.667);
      }
      double U1 = 0;
      if (B1 != 0) {
        U1 = C1 * Math.pow(B1, -0.333);
      }
      double C2 = 0;
      double NG = Data.DATA_SIZE * Data.DATA_INTERVAL * TOTAL_SLOTS / (double)TIME_SLOT;
      if (R2 >= NG) {
        C2 = 0;
      }
      else if (B2 != 0) {
        C2 = -NG / B2 * Math.log(1 - R2 / NG);
      }
      double U2 = C2 * Math.exp(- C2 * B2 / NG);
      if (U1 == 0 && U2 == 0) {
        return 0.5;
      }
      double B1p = Math.pow((U1+U2)/(2*C1), -3);
      //System.out.println(B1p + " " + B1 + " " + B2);
      B1p /= B1 + B2;

      /*adjust*/
      B1p = 0.3 + (B1p - 0.3) * 2;

      if (B1p < 0) return 0;
      if (B1p > 1) return 1;
      return B1p;
    }

  }

  protected Traffic[] history;
  protected Traffic total;
  protected Node node;

  public TimeStats(Node node) {
    this.node = node;
    total = new Traffic();
    total.dtdvMaxHop = 5;
    history = new Traffic[TOTAL_SLOTS];
    for (int i = 0; i < TOTAL_SLOTS; ++ i) {
      history[i] = new Traffic();
    }
    Clock.start(this, TIME_SLOT, false);
  }


  public void timeout() {
    total.dtdvMaxHop = 0;
    for (int i = 0; i < TOTAL_SLOTS; ++ i) {
      if (total.dtdvMaxHop < history[i].dtdvMaxHop) {
        total.dtdvMaxHop = history[i].dtdvMaxHop;
      }
    }
    /* DTDV maxHop */
    node.dtdv.setMaxHop(total.dtdvMaxHop + 1);
    /**/
    total.decrease(history[TOTAL_SLOTS - 1]);
    total.increase(history[0]);
    for (int i = TOTAL_SLOTS - 1; i >= 1; -- i) {
      history[i] = history[i - 1];
    }
    history[0] = new Traffic();
    Clock.start(this, TIME_SLOT, false);
  }

  public double dtdvUtility() {
    return total.dtdvUtility();
  }

  public double swUtility() {
    return total.swUtility();
  }

  public double currentDtdvAllocationPoint() {
    return total.currentDtdvAllocationPoint();
  }

  public double currentDtdvAllocationPoint2() {
    return total.currentDtdvAllocationPoint2();
  }

  public double newDtdvAllocationPoint() {
    return total.newDtdvAllocationPoint();
  }

  public void incDtdvMsg(int size) {
    history[0].incDtdvMsg(size);
  }

  public void incDtdvSendDataMsg(int size) {
    history[0].incDtdvSendDataMsg(size);
  }

  public void incSwSendDataMsg(int size) {
    history[0].incSwSendDataMsg(size);
  }

  public void incRecvDataMsg(int size, int tickets, int hops) {
    history[0].incRecvDataMsg(size, tickets, hops);
  }

}
